import random
import string
import time
from datetime import datetime
from functools import wraps
from json import loads
import re

from flask import request, g

from core.base_error import ArgsMissError, ArgsErrError
from core.base_tool import get_login
from core.output import OutputHttpResponse


def check_args(param_key, method=None, param_type='str', default=None, able_none=False, max_length=None,
               min_length=None, tip_name=None):
    """
    预处理输入的数据
    :param param_key: 参数key
    :param method: 方法，GET或者POST
    :param param_type: 参数类型，支持str,int,uint,float,dict,json,datetime,date,time,phone,list
    :param default: 默认值 有默认值的清空下允许参数为空
    :param able_none: 是否允许空参数
    :param max_length: 字符串类型专属，最大长度
    :param min_length: 字符串类型专属，最少长度
    :param tip_name: 需要提示的参数名字
    :return:
    """
    if method is None:
        method = request.method
    if method == 'GET':
        data = request.args.get(param_key)
    else:
        data = request.form.get(param_key)

    if data is None or data == str():
        data = default
        if data is not None:
            return data

    if able_none and data is None:
        return None

    if data is None:
        err = ArgsMissError("参数<%s>缺失~" % param_key)
        if tip_name:
            err.http_return_msg = "参数<%s>缺失~" % tip_name

        raise err

    err = ArgsErrError("参数<%s>错误~" % param_key)
    if tip_name:
        err.http_return_msg = "参数<%s>错误~" % tip_name

    # 解析一部分类型 会抛异常
    try:
        if param_type == 'int' or param_type == 'uint':
            data = int(data)
        elif param_type == 'float':
            data = float(data)
        elif param_type == 'dict':
            data = loads(data)
            # 数字字符经过loads()会变成int，非dict，需要抛出异常
        elif param_type == 'json':
            loads(data)
            # 数字字符经过loads()没有异常，非json，需要抛出异常
        elif param_type == 'datetime':
            data = datetime.strptime(data, "%Y-%m-%d %H:%M:%S")
        elif param_type == 'date':
            data = datetime.strptime(data, "%Y-%m-%d").date()
        elif param_type == 'time':
            data = datetime.strptime(data, "%H:%M:%S").time()
        elif param_type == 'list':
            data = loads(data)
    except:
        raise err

    # 手动解析一部分类型 手动抛异常
    if param_type == 'uint' and data < 0:
        raise err
    if param_type == 'dict' and not isinstance(data, dict):  # 有可能是数字
        raise err
    if param_type == 'json' and isinstance(loads(data), int):  # 有可能是数字
        raise err
    if param_type == "phone":
        reg = r'^\+?(?:(?:00)?86)?1\d{10}$'
        if not re.match(reg, str(data)):
            raise err
    if param_type == 'list':
        if not isinstance(data, list):
            raise err
    if param_type != 'str':
        return data

    # 下面是限制str类型的代码
    length = len(data)
    if min_length is not None and length < min_length:
        err = ArgsErrError("参数%s错误~" % param_key)
        if tip_name:
            err.http_return_msg = "参数%s长度不允许少于%d" % (tip_name, min_length)
        else:
            err.http_return_msg = "参数%s长度不允许少于%d" % ('', min_length)
        raise err

    if max_length is not None and length > max_length:
        err = ArgsErrError("参数%s错误~" % param_key)
        if tip_name:
            err.http_return_msg = "参数%s长度不允许大于%d" % (tip_name, max_length)
        else:
            err.http_return_msg = "参数%s长度不允许大于%d" % ('', max_length)
        raise err

    return data


random_str_list = ['3', '8', 'i', 'b', 'a', 'o', 'l', 'x', 's', 'h', 'j', 'y', 'z', 'c', '7', 't', '9', '0', 'n', '5',
                   'u', 'w', 'd', 'v', '2', '4', 'r', 'f', 'e', 'q', 'k', 'g', 'm', 'p', '6', '1']


def get_short_code():
    """获取一段12位短码，时间戳转36进制"""
    n = str(int(time.time() * 100000))
    n = list(n)

    # 位置互换，避免生成相似
    n[13], n[2] = n[2], n[13]
    n[9], n[0] = n[0], n[9]
    n[6], n[1] = n[1], n[6]

    n = int(''.join(n))

    x = 36
    b = []
    while True:
        s = n // x  # 商
        y = n % x  # 余数
        b = b + [random_str_list[y]]
        if s == 0:
            break
        n = s
    s = ''.join(b)

    while len(s) < 12:
        c = random.choice(string.ascii_lowercase + string.digits)
        s = s.rjust(len(s) + 1, c)

    return s


def get_for_page(query, page: int, nums: int):
    """
    获取一页记录列表
    :param query: 搜索表达式
    :param page: 页数，第一页为1
    :param nums: 每页数量
    :return:
    """
    if page <= 0:
        page = 1
    offset = (page - 1) * nums
    return query.offset(offset).limit(nums)


def check_login(fn):
    """检查登录态"""

    @wraps(fn)
    def handle(*k, **v):
        token = request.headers.get('token', None)
        if not token:
            return OutputHttpResponse.need_login_error()
        uid, phone = get_login(token)

        if not uid:
            return OutputHttpResponse.need_login_error()

        if not phone:
            return OutputHttpResponse.need_login_error()

        g.uid = uid
        g.phone = phone
        g.token = token

        return fn(*k, **v)

    return handle
